#include <iostream>
#include <unistd.h>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
using namespace std;
enum class Status //c++11的强类型类 
{
    NEW,
    CONNECTING, 
    CONNECTED, //connect成功之后的状态, 此时直接执行process
    DISCONNECTED, //当我们第一次连接失败/运行的时候服务器突然掉线的时候需要进行重连
    CLOSED //连接彻底断开, 直接释放文件描述符
};

enum Exitcode
{
    USEAGE_ERROR,
    SOCK_ERROR
};
const int defaultsockfd = -1;
const int defaultretryinterval = 1;
const int defaultmaxretries = 5;
class ClientConnection
{   
public:
    ClientConnection(const string& serverip,uint16_t serverport)
    :_sockfd(defaultsockfd),
    _serverip(serverip),
    _serverport(serverport),
    _retry_interval(defaultretryinterval),
    _max_retries(defaultmaxretries),
    _status(Status::NEW)
    {}
    void Connect(){
        _sockfd = ::socket(AF_INET,SOCK_STREAM,0);
        if (_sockfd < 0){
            cout << "socket creat error" << endl;
            exit(Exitcode::SOCK_ERROR); //创建套接子失败了, 直接退出
        }
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        server.sin_addr.s_addr = inet_addr(_serverip.c_str());
        // ::inet_pton(AF_INET,_serverip.c_str(),&server.sin_addr); //最后一个参数是把网络序列的结果写到哪里
        int n = connect(_sockfd,(struct sockaddr*)(&server),sizeof(server)) ;
        if (n < 0){
            //1. 释放原来的socket (建议释放)
            Close();
            //2. 改变状态 -> Status::DISCONNECTED -> 连接断开的状态
            _status = Status::DISCONNECTED;
            //3. 返回
            return;
        }
        //连接成功
        cout << "connect success" << endl;
        _status = Status::CONNECTED;
    }
    void Reconnect(){
        _status = Status::CONNECTING; //正在连接的状态, 外部不会使用
        int cnt = 0;
        while(true){
            //其实就是调用Connect()
            cout << "正在重新连接: " << cnt << "次"<< endl;
            Connect();
            cnt++;
            if (_status == Status::CONNECTED){
                //如果连接建立成功了,就退出去
                break;
            }
            if (cnt > _max_retries){
                cout << "重连失败, 请检查您的网络...." << endl;
                _status = Status::CLOSED;
                return;
            }
            sleep(_retry_interval);
        }
    }
    void Process(){
        while(1){
            //这里消息直接写定
            string message = "hello server";
            int n = send(_sockfd,message.c_str(),message.size(),0);
            if (n > 0){
                char buffer[1024];
                int m = ::recv(_sockfd,buffer,sizeof(buffer)-1,0);
                if (m > 0){
                    buffer[m] = 0;
                    cout << buffer << endl;
                }
                else {
                    _status = Status::DISCONNECTED;
                    break;
                }
            }
            else {
                //这个时候直接断开连接就可以了, 一般不会遇到这种错误
                _status = Status::CLOSED;
               break;
            }
            sleep(1);
        }
    } //进行IO
    void Disconnect(){
        Close();
        Reconnect();
    }
    void Close(){
        if (_sockfd != defaultsockfd){
            ::close(_sockfd);
            _sockfd = defaultsockfd;
        }
    }
    Status Getstatus(){
        return _status;
    }
    ~ClientConnection(){}

private:
    Status _status; //连接状态
    int _sockfd; 
    uint16_t _serverport;
    string _serverip;
    int _retry_interval; //重连的间隔
    int _max_retries; //重连的最大次数
};

class TcpClient
{
public:
    TcpClient(const string& serverip,uint16_t serverport)
        :_connection(serverip,serverport)
    {}
    void Execute(){
        while(1){
            switch(_connection.Getstatus()){
                case Status::NEW:
                _connection.Connect();
                break;
                case Status::CONNECTED:
                _connection.Process();
                break;
                case Status::DISCONNECTED:
                _connection.Reconnect();
                break;
                case Status::CLOSED:
                _connection.Close();
                return;
                default:
                //do nothing
                break;
            }
        }
    }
    ~TcpClient(){}
private:
    ClientConnection _connection;
};

void Useage(const string& message){
    cout << "Useage: " << message << "+ serverip + serverport" << endl;
}

int main(int argc,char* argv[]){
    if (argc != 3){
        Useage(argv[0]);
        exit(Exitcode::USEAGE_ERROR);
    }
    string serverip = argv[1];
    uint16_t serverport = stoi(argv[2]);
    TcpClient client(serverip,serverport);
    client.Execute();
    return 0;
}



